home *** CD-ROM | disk | FTP | other *** search
- Date: Thu, 04 Oct 84 14:51:26 EDT
- From: Edgar B. Butt <BUTT@UMD2.ARPA>
- To: sy.fdc@cu20b
- Subject: Oh no, another Kermit!
-
- Here is a Kermit implementation for the Sperry 1100 systems written
- in Pascal. It has been run successfully here at the University of Maryland,
- College Park, and at SUNY, Albany. Please add it to your selection
- of Kermits. I would appreciate feedback from anyone who tries it.
-
- The first page of code consists of comments explaining how to
- use and generate Kermit1100.
-
- Hop someone finds it useful,
-
- Edgar Butt (Butt@umd2.arpa)
- Computer Science Center
- University of Maryland
- College Park, Maryland 20742
- (301) 454-2946
-
- The source for Kermit1100 version 2.0 begins on the next line.
- {Kermit1100 - see first executable line in main block for version
-
- KERMIT1100 is yet another Kermit written to run on the Sperry (Univac)
- 1100 series of computers. It is written in Pascal to be compiled on
- the NOSC Pascal Compiler, version 2.2 or later. This compiler is
- available from the Computer Science Center of the University of
- Maryland, College Park, for a nominal service charge.
-
- Kermit aficianodos may notice that the structure of this version
- differs from other versions in that packets are read and sequence
- checked in the main program loop and are then dispatched to the
- proper input or output state with a single case statement.
- This structure has allowed the various state processes to be
- relatively uncluttered. While doing this implementation I
- discovered that NAK's are like tadpole tails. They seem like
- a neat idea at first, but as the frog emerges, they serve no
- useful purpose. Likewise, I have been unable to find a case
- in which NAK's are necessary. Sending an ACK for the last
- good packet received is just as good. If I'm wrong, I am sure
- that some swamp dweller out there will let me know.
- (Not to worry, I handle incoming NAK's even though they are not
- necessary.)
-
- By way of a quick synopsys of features, this version of Kermit has:
-
- Simple server mode - processes S and R packets
- 8-bit quoting (Turned on by Q-option)
- Repeat count prefixes
- Error packet generation and processing
-
- Kermit 1100 is called as a processor with the following control card:
-
- @Q*F.KERMIT,OPTIONS 1100SPEC,REMOTESPEC
-
- Q*F. is the file in which the processor resides.
- 1100SPEC is the 1100 file or element on which Kermit will operate.
- REMOTESPEC is the file name sent to the remote Kermit(a fib of sorts)
- OPTIONS:
- B - big buffers. Kermit1100 normally tells the remote Kermit to send
- packets that will fit in 84 characters. B-option causes it to
- request the maximum size Kermit packets (which ain't as big as you
- might wish) Make sure that your communications hardware and
- software will let the long packets get through.
- C - assume for sending or receiving that records are to be separated
- by CR instead of CR-LF
- L - log in the element KERMITLOG.MDSSS all file reads and writes and
- all communication sends and receives. MDSSS is the month, day and
- seconds/4 encoded base 32 (0,...,9,A,...,V). If a catalogued file
- 'KERMITLOG' is assignable, it is used. Otherwise a temporary file
- is created.
- Q - allow eight-bit quoting for sending or receiving. If the file
- being sent or received has 8-bit data and if the remote kermit
- is capable of 8-bit quoting, then all 8-bits of data can be
- sent or received.
- R - expect to receive data. Put the data in 1100SPEC if specified
- or in the file or element name sent from the remote Kermit. No
- transformation on the incoming name is done at present so it
- had better be good.
- S - send 1100SPEC to the remote Kermit. If REMOTESPEC is specified,
- put it in the file header packet. Otherwise put 1100SPEC in the
- packet.
- T - test mode. Send (actually print on a terminal) packets as if
- an S-option had been specified without reading ACK's.
- W - If the S-option is used, wait 30 seconds before starting to send
-
- Kermit1100 tries not to exit until an EOF is received in order to process
- multiple requests from the remote Kermit.
-
- Happy hopping,
-
- Edgar Butt (BUTT@UMD2.ARPA)
- Computer Science Center
- University of Maryland
- College Park, Maryland 20742
- Phone (301) 454-2946
-
- }
- {$F Here we go.....}
-
- PROCESSOR Kermit (input, output);
-
- CONST
- maxtry = 5;
- maxbuf = 200 ;
- maxlin = 80;
- maxwrt = 132;
-
- ascnul = 0;
- ascsoh = 1;
- asclf = 10;
- asccr = 13;
- ascsp = 32; { }
- ascns = 35; {#}
- ascamp = 38; {&}
- ascast = 42; {*}
- ascper = 46; {.}
- ascb = 66; {B}
- ascd = 68; {D}
- asce = 69; {E}
- ascf = 70; {F}
- ascn = 78; {N}
- ascr = 82; {R}
- ascs = 83; {S}
- asct = 84; {T}
- ascy = 89; {Y}
- ascz = 90; {Z}
- asctil = 126; {~}
- ascdel = 127; {rubout}
-
- mark = ascsoh;
-
- TYPE
- kermitstates = (kcommand,
- kexit,
- wexit,
- sinitiate,
- sheader,
- sdata,
- sbreak,
- rinitiate,
- rheader,
- rdata);
- filestatus = (closed, open, endfile);
- ascval = 0..255 ;
- ascbuf = RECORD
- ln: INTEGER;
- ch: ARRAY[1..maxbuf] OF ascval;
- END;
- line = PACKED ARRAY [1..maxlin] OF CHAR;
-
- {System dependent TYPE}
-
- ident= PACKED ARRAY[1..12] OF CHAR;
- sbits = SET of 0..35;
-
- VAR
-
- version: string;
- iniflg: boolean; {Set true after first initialization}
- server: boolean; {If true, Kermit1100 waits for packets from remote}
- state: kermitstates;
- filbuf,wrtbuf,redbuf,sndbuf,rcvbuf: ascbuf;
- redix: integer;
- rfile,wfile,lfile: text;
- fname,rfname,lname: line;
- fnlen,rfnlen: INTEGER;
- rstatus, wstatus,lstatus: filestatus;
- seq,rcvseq: INTEGER;
- rlen: INTEGER;
- stype,rcvtyp: ascval;
- numtry: INTEGER;
- numcserr: INTEGER;
- ineoln: boolean;
- sndonly: boolean;
- sndlog, rcvlog, wrtlog, redlog: boolean;
- bstrip: boolean;
- creol: boolean;
- lfeol: boolean;
- crlfeol: boolean;
- gotcr: boolean;
-
- locbsiz: ascval;
- loctout: ascval;
- locnpad: ascval;
- locpad: ascval;
- loceol: ascval;
- locquo: ascval;
- optqu8: ascval;
- locqu8: ascval;
- locrep: ascval;
-
- rembsiz: ascval;
- remdsiz: ascval; {Maximum number of data characters to send (remdsiz-3)}
- remtout: ascval;
- remnpad: ascval;
- rempad: ascval;
- remeol: ascval;
- remquo: ascval;
- remqu8: ascval;
- remrep: ascval;
-
- {System dependent VAR}
- ruse,wuse,luse: ident;
- a0,a1,a2: integer;
-
- {Forward reference procedures }
-
- PROCEDURE error(msg:string);FORWARD;
-
- {System dependent procedures to read and write files}
-
- PROCEDURE readelt1(VAR f:text; filename:ident; name:line; VAR ok:boolean);
- EXTERN;
- PROCEDURE openelt1(VAR f:text; filename:ident; name:line; VAR ok:boolean);
- EXTERN;
- PROCEDURE closeelt1(VAR f:text; filename:ident; name:line); EXTERN;
- PROCEDURE param_string(field:INTEGER; VAR param:STRING); EXTERN;
- PROCEDURE csf(image:line; VAR status:sbits);EXTERN;
- PROCEDURE write_now(VAR f:text);EXTERN;
-
- {
- System dependent procedure to get a file name from the procedure call card.
- }
- PROCEDURE getspec(field: INTEGER; VAR l: line; VAR len: INTEGER);
- VAR s: string[80];
- i: INTEGER;
-
- BEGIN
- param_string(field,s);
- len:=LENGTH(s);
- FOR i:=1 TO len DO l[i]:=s[i];
- FOR i:=len+1 TO 80 DO l[i]:=' ';
- END;
-
- {$F Character manipulation routines}
-
- {System dependent: It is assumed that the function ord(c) where
- c is of type char will return the ASCII code for the character c.}
-
- {System dependent: It is assumed that the function chr(i) where
- i is an integer ASCII code from 0 to 255 will return the appropriate
- character}
-
- FUNCTION makechar (i: INTEGER): ascval;
-
- BEGIN
- makechar:=ascsp+i;
- END;
-
- FUNCTION unchar (a: ascval): INTEGER;
-
- BEGIN
- unchar:=a-ascsp;
- END;
-
- FUNCTION tog64(a: ascval): ascval;
-
- BEGIN
- tog64:=bxor(64,a); {System dependent}
- END;
-
- FUNCTION tog128(a: ascval): ascval;
-
- BEGIN
- tog128:=bxor(128,a); {System dependent}
- END;
-
- FUNCTION checksum (sum: INTEGER): ascval;
-
- BEGIN
- checksum := (((sum MOD 256) DIV 64) + sum) MOD 64;
- END;
- {$F Open and close log file}
- PROCEDURE logopn; {System dependent}
- VAR i,t: INTEGER;
- lstat: boolean;
- csfsta: sbits;
-
- BEGIN
- csf('@asg,az kermitlog. ',csfsta);
- IF 35 IN csfsta THEN
- BEGIN
- csf('@asg,t kermitlog.,///256 . ',csfsta);
- END;
- IF 35 IN csfsta THEN
- BEGIN
- writeln(lfile,'Error assigning logfile: KERMITLOG');
- END
- ELSE
- BEGIN
- lname:='KERMITLOG.mdttt . ';
- er(44{TDATE$},a0);
- a1:=bshr(band(170000000000b,a0),10)+bshr(band(3700000000b,a0),9)
- +band(77777b,bshr(a0,2));
- FOR i:=1 TO 5 DO
- BEGIN
- t:=band(31,bshlc(a1,11+5*i))+48;
- IF t>57 THEN t:=t+7;
- lname[10+i]:=chr(t);
- END;
- luse:='L$F$I$L$E$$$';
- openelt1(lfile,luse,lname,lstat);
- IF lstat=false THEN
- BEGIN
- writeln('Error opening log element: ',lname);
- END
- ELSE
- BEGIN
- lstatus:=open;
- write(lfile,'Kermit1100 ',version,' Logfile ');
- write_now(lfile); {Write date and time into logfile}
- writeln(lfile);
- writeln(output,'Logging to ',lname);
- END;
- END;
- END;
-
- PROCEDURE logcls; {System dependent}
-
- BEGIN
- IF lstatus=open THEN
- BEGIN
- closeelt1(lfile,luse,lname);
- END;
- END;
- {$F Buffer routines}
-
- PROCEDURE bufinit(VAR buf:ascbuf);
-
- BEGIN
- buf.ln:=0;
- END;
-
- PROCEDURE putbuf(VAR buf: ascbuf; a:ascval);
-
- BEGIN
- IF NOT (buf.ln<maxbuf) THEN
- BEGIN
- error('Size of ascii buffer exceeded');
- END
- ELSE
- BEGIN
- buf.ln:=buf.ln+1;
- buf.ch[buf.ln]:=a;
- END;
- END;
-
- PROCEDURE lintobuf(l: line; len: integer; VAR buf: ascbuf);
- VAR i:integer;
-
- BEGIN
- bufinit(buf);
- FOR i:=1 TO len DO putbuf(buf,ord(l[i]));
- END;
-
- PROCEDURE buftolin(buf: ascbuf; VAR l: line; VAR len: integer);
- VAR i:integer;
- a:ascval;
-
- BEGIN
- len:=buf.ln;
- IF len>maxlin THEN len:=maxlin;
- FOR i:=1 TO len DO
- BEGIN
- a:=buf.ch[i];
- IF a>127 THEN a:=a-127;
- l[i]:=chr(a);
- END;
- FOR i:=len+1 to maxlin DO l[i]:=' ';
- END;
- {$F Process parameters to and from remote Kermit}
- PROCEDURE putpar;
- VAR temp: ascval;
-
- BEGIN
- bufinit(filbuf);
- putbuf(filbuf,makechar(locbsiz));
- putbuf(filbuf,makechar(loctout));
- putbuf(filbuf,makechar(locnpad));
- putbuf(filbuf,tog64(locpad));
- putbuf(filbuf,makechar(loceol));
- putbuf(filbuf,locquo);
- temp:=ascsp;
- IF locqu8<>0 THEN temp:=locqu8;
- putbuf(filbuf,temp);
- putbuf(filbuf,ascsp); {Only know how do to 1 character checksum}
- temp:=ascsp;
- IF locrep<>0 THEN temp:=locrep;
- putbuf(filbuf,temp);
- END;
-
- PROCEDURE getpar;
-
- BEGIN
- IF rcvbuf.ln > 0 THEN rembsiz:=unchar(rcvbuf.ch[1]);
- IF rcvbuf.ln > 1 THEN remtout:=unchar(rcvbuf.ch[2]);
- IF rcvbuf.ln > 2 THEN remnpad:=unchar(rcvbuf.ch[3]);
- IF rcvbuf.ln > 3 THEN rempad:=tog64(rcvbuf.ch[4]);
- IF rcvbuf.ln > 4 THEN remeol:=unchar(rcvbuf.ch[5]);
- IF rcvbuf.ln > 5 THEN remquo:=rcvbuf.ch[6];
- IF rcvbuf.ln > 6 THEN remqu8:=rcvbuf.ch[7];
- IF rcvbuf.ln > 8 THEN remrep:=rcvbuf.ch[9];
-
- remdsiz:=rembsiz-3;
- IF state=rinitiate THEN {Our parameters have not been sent}
- BEGIN
- IF locqu8=0 THEN remqu8:=0;
- IF ((32<remqu8) AND (remqu8<63)) OR ((95<remqu8) AND (remqu8<127))
- AND (remqu8<>remquo) THEN
- BEGIN
- locqu8:=ascy; {Remote Kermit specified 8-bit quote character}
- END
- ELSE IF remqu8=ascy THEN
- BEGIN
- locqu8:=ascamp;
- IF (locqu8=remquo) OR (locqu8=remrep) THEN locqu8:=asctil;
- IF (locqu8=remquo) OR (locqu8=remrep) THEN locqu8:=ascns;
- remqu8:=locqu8;
- END
- ELSE
- BEGIN
- locqu8:=0; {Don't do 8-bit quoting}
- remqu8:=0;
- END;
- IF ((32<remrep) AND (remrep<63)) OR ((95<remrep) AND (remrep<127))
- AND (remrep<>remquo) AND (remrep<>remqu8) AND (locrep<>0) THEN
- BEGIN
- locrep:=remrep; {Agree to do repeat counts}
- END
- ELSE
- BEGIN
- remrep:=0;
- locrep:=0;
- END;
- END
- ELSE {Our parameters have already been sent}
- BEGIN
- IF (remqu8<>ascy) AND (remqu8<>locqu8) THEN
- BEGIN
- locqu8:=0; {Don't do 8-bit quoting}
- END;
- IF remrep<>locrep THEN locrep:=0; {Don't do repeat counts}
- END;
- END;
- {$F Input a packet or a command}
- PROCEDURE rcvpkt;
- {
- This procedure reads all terminal input to Kermit, both packets and
- command lines. On exit, the following global parameters are set:
-
- rcvtyp = 0 - No SOH encountered, could be command line
- 1 - SOH encountered, but packet incomplete
- 2 - Checksum error
- Other - ASCII value of packet type from good packet
-
- rcvseq = -1 - Not a valid packet
- -2 - End of input file encountered
- 0...63 - Sequence number from valid packet
-
- rcvbuf.ln - number of ascii values input since last SOH or
- if no SOH, from beginning of line
- rcvbuf.ch - array of ascii values input
- }
- VAR c: CHAR;
- av,rt: ascval;
- rst,rsq,cs:INTEGER;
-
- BEGIN
- IF rcvlog THEN write(lfile,'rcv <');
- IF ineoln THEN
- BEGIN
- readln(input);
- END;
- rcvtyp:=0;
- IF eof(input) THEN
- BEGIN
- rcvseq:=-2;
- IF rcvlog THEN write(lfile,'@');
- END
- ELSE
- BEGIN
- rcvseq:=-1;
- rst:=0;
- ineoln:=eoln(input);
- bufinit(rcvbuf);
- WHILE NOT ineoln DO
- BEGIN
- IF eoln(input) THEN
- BEGIN
- {
- The 1100 EXEC truncates some trailing spaces. Since a
- valid packet can end in one or more spaces, we will assume
- that short packets should end in spaces and hope that
- the checksum filters out errors.
- }
- av:=ascsp;
- END
- ELSE
- BEGIN
- read(input,c);
- IF rcvlog THEN write(lfile,c);
- av:=ord(c);
- END;
- IF av=mark THEN rst:=1;
- CASE rst OF
-
- 0: {Mark character never encountered.}
- BEGIN
- putbuf(rcvbuf,av);
- ineoln:=eoln(input);
- END;
-
- 1: {Mark character.}
- BEGIN
- rcvtyp:=1;
- rcvseq:=-1;
- bufinit(rcvbuf);
- ineoln:=eoln(input);
- rst:=2;
- END;
-
- 2: {Length of the packet.}
- BEGIN
- cs:=av; {Initialize checksum}
- rlen:=unchar(av)-3;
- rst:=3;
- END;
-
- 3: {Packet number.}
- BEGIN
- cs:=cs+av;
- rsq:=unchar(av);
- rst:=4;
- END;
-
- 4: {Packet type.}
- BEGIN
- cs:=cs+av;
- rt:=av; {remember the packet type}
- rst:=5;
- IF rlen=0 THEN rst:=6;
- END;
-
- 5: {Data portion.}
- BEGIN
- cs:=cs+av;
- putbuf(rcvbuf,av);
- IF rcvbuf.ln = rlen THEN rst:=6;
- END;
-
- 6: {Checksum.}
- BEGIN
- IF checksum(cs)=unchar(av) THEN
- BEGIN
- rcvtyp:=rt;
- rcvseq:=rsq;
- ineoln:=true; {Ignore the rest of the line}
- END
- ELSE
- BEGIN
- numcserr:=numcserr+1;
- rst:=0; {Look for another mark}
- rcvtyp:=2; {Indicate checksum error}
- ineoln:=eoln(input);
- END;
- END;
- END;
- END;
- END;
- IF rcvlog THEN writeln(lfile,'>');
- END;
- {$F Build and send packets}
- PROCEDURE makepacket(ptype: ascval; seq, len: INTEGER);
- VAR i: INTEGER;
- c: ascval;
- cs: INTEGER;
-
- BEGIN
- bufinit(sndbuf);
- FOR i:=1 TO remnpad DO
- BEGIN
- putbuf(sndbuf,rempad);
- END;
- putbuf(sndbuf,mark);
- c:=makechar(len+3);
- cs:=c; {Initialize checksum}
- putbuf(sndbuf,c);
- c:=makechar(seq);
- cs:=cs+c;
- putbuf(sndbuf,c);
- c:=ptype;
- cs:=cs+c;
- putbuf(sndbuf,c);
- FOR i:=1 to len DO
- BEGIN
- c:=filbuf.ch[i];
- cs:=cs+c;
- putbuf(sndbuf,c);
- END;
- c:=makechar(checksum(cs));
- putbuf(sndbuf,c);
- {
- The 1100 EXEC may strip trailing spaces from the end of output images.
- This can cause a problem if the checksum is a space. To eliminate this
- problem, a period will be inserted in the output image after the
- checksum whenever the checksum is a space.
- }
- putbuf(sndbuf,ascper);
- {
- The 1100 O/S puts a CR LF on the end of each output line.
- If the remote EOL character is not CR or LF, then it must
- be added to the packet.
- }
- IF (remeol<>asccr) AND (remeol<>asclf) THEN
- BEGIN
- putbuf(sndbuf,remeol);
- END;
- END;
-
- PROCEDURE sndpkt;
- VAR
- i:INTEGER;
-
- BEGIN
- IF sndlog THEN write(lfile,'snd <');
- FOR i:=1 TO sndbuf.ln DO
- BEGIN
- write(output,chr(sndbuf.ch[i]));
- IF sndlog THEN write(lfile,chr(sndbuf.ch[i]));
- END;
- writeln(output);
- IF sndlog THEN writeln(lfile,'>');
- END;
- {$F File output}
-
- PROCEDURE wrtrec;
- VAR
- i:INTEGER;
- c:char;
-
- BEGIN
- IF wrtlog THEN write(lfile,'wrt [');
- FOR i:=1 TO wrtbuf.ln DO
- BEGIN
- {$A- Turn off range checking, ASCII value may be >127}
- c:=chr(wrtbuf.ch[i]);
- {$A+ Turn on range checking}
- write(wfile,c) ;
- IF wrtlog THEN write(lfile,c);
- END;
- writeln(wfile);
- IF wrtlog THEN writeln(lfile,']');
- bufinit(wrtbuf);
- END;
-
- PROCEDURE wrtcls; {System dependent}
-
- BEGIN
- IF wstatus=open THEN
- BEGIN
- IF wrtbuf.ln>0 THEN wrtrec;
- closeelt1(wfile,wuse,fname);
- END;
- wstatus:=closed;
- END;
-
- PROCEDURE wrtopn; {System dependent}
- VAR
- wstat: boolean;
-
- BEGIN
- wrtcls;
- wuse:='W$F$I$L$E$$$';
- openelt1(wfile,wuse,fname,wstat);
- IF wstat THEN wstatus:=open;
- bufinit(wrtbuf);
- END;
-
- PROCEDURE wrtasc(a:ascval);
-
- BEGIN
- IF wrtbuf.ln >=maxwrt THEN wrtrec;
- putbuf(wrtbuf,a);
- END;
- {$F Process data portion of data packet}
- PROCEDURE putrec(buf: ascbuf);
- VAR
- i,j,repcnt:INTEGER;
- a:ascval;
- qflag: boolean;
-
- BEGIN
- i:=1;
- WHILE i<= buf.ln DO
- BEGIN
- a:=buf.ch[i]; i:=i+1;
- repcnt:=1;
- IF a=remrep THEN
- BEGIN
- repcnt:=unchar(buf.ch[i]); i:=i+1;
- a:=buf.ch[i]; i:=i+1;
- END;
- qflag:= a=remqu8;
- IF qflag THEN
- BEGIN
- a:=buf.ch[i]; i:=i+1;
- END;
- IF a=remquo THEN
- BEGIN
- a:=buf.ch[i]; i:=i+1;
- IF (a<>remquo) AND (a<>remqu8) AND (a<>remrep) THEN a:=tog64(a);
- END;
- IF qflag THEN a:=tog128(a);
- FOR j:=1 to repcnt DO
- BEGIN
- IF a=asclf THEN
- BEGIN
- IF lfeol OR gotcr THEN
- BEGIN
- wrtrec;
- gotcr:=false;
- END
- ELSE
- BEGIN
- wrtasc(a);
- END;
- END
- ELSE
- BEGIN
- IF gotcr THEN
- BEGIN
- wrtasc(asccr);
- gotcr:=false;
- END;
- IF a=asccr THEN
- BEGIN
- IF creol THEN
- BEGIN
- wrtrec;
- END
- ELSE IF crlfeol THEN
- BEGIN
- gotcr:=true;
- END
- ELSE
- BEGIN
- wrtasc(a);
- END;
- END
- ELSE
- BEGIN
- wrtasc(a);
- END;
- END;
- END;
- END;
- END;
- {$F File input}
- PROCEDURE redrec;
- VAR c: CHAR;
- a: ascval;
- nonblank: INTEGER;
-
- BEGIN
- bufinit(redbuf);
- IF redix >= 0 THEN readln(rfile);
- redix:=0;
- IF NOT eof(rfile) THEN
- BEGIN
- nonblank:=0;
- IF redlog THEN write(lfile,'red [');
- WHILE NOT eoln(rfile) DO
- BEGIN
- read(rfile,c);
- IF redlog THEN write(lfile,c);
- a:=ord(c);
- putbuf(redbuf,a);
- IF a <> ascsp THEN nonblank := redbuf.ln;
- END;
- IF redlog THEN writeln(lfile,']');
- IF bstrip THEN redbuf.ln := nonblank;
- IF creol OR crlfeol THEN putbuf(redbuf,asccr);
- IF lfeol OR crlfeol THEN putbuf(redbuf,asclf);
- END;
- END;
-
- PROCEDURE redopn; {System dependent}
- VAR
- rstat: boolean;
-
- BEGIN
- rstatus:=closed;
- ruse:='R$F$I$L$E$$$';
- readelt1(rfile,ruse,fname,rstat);
- IF rstat THEN rstatus:=open;
- redix:=-1;
- redbuf.ln:=-1;
- END;
-
- PROCEDURE redcls;
-
- BEGIN
- rstatus:=closed;
- END;
-
- {$F Build data portion of data packet}
- PROCEDURE getrec;
- VAR a: ascval;
- exit: BOOLEAN;
- prevln,previx,tix: INTEGER;
-
- BEGIN
- bufinit(filbuf);
- IF eof(rfile) THEN
- BEGIN
- rstatus:=endfile;
- END
- ELSE
- BEGIN
- exit:=false;
- REPEAT
- IF redix >= redbuf.ln THEN
- BEGIN
- redrec; {get another record and strip spaces}
- IF eof(rfile) THEN
- BEGIN
- exit:=true;
- IF filbuf.ln=0 THEN rstatus:=endfile;
- END;
- END;
- IF redix < redbuf.ln THEN
- BEGIN
- prevln:=filbuf.ln;
- previx:=redix;
- redix:=redix+1;
- a:=redbuf.ch[redix];
- IF locrep<>0 THEN
- BEGIN
- tix:=redix+1;
- WHILE (a=redbuf.ch[tix]) AND (tix<=redbuf.ln) DO tix:=tix+1;
- tix:=tix-redix; {tix is now the repeat count}
- IF tix>3 THEN
- BEGIN
- IF tix>94 THEN tix:=94;
- putbuf(filbuf,locrep);
- putbuf(filbuf,makechar(tix));
- redix:=redix-1+tix;
- END;
- END;
- IF (a>127) THEN
- BEGIN
- IF locqu8<>0 THEN putbuf(filbuf,locqu8);
- a:=tog128(a);
- END;
- IF (a<32) OR (a=ascdel) THEN
- BEGIN
- putbuf(filbuf,locquo);
- a:=tog64(a);
- END;
- IF (a=locquo) OR (a=locqu8) OR (a=locrep) THEN
- BEGIN
- putbuf(filbuf,locquo);
- END;
- putbuf(filbuf,a);
- IF filbuf.ln >= remdsiz THEN
- BEGIN
- exit:=true;
- IF filbuf.ln>remdsiz then
- BEGIN
- {Character expansion caused buffer length to be
- exceeded. Back up.}
- filbuf.ln:=prevln;
- redix:=previx;
- END;
- END;
- END;
- UNTIL exit;
- END;
- END;
-
- {$F Send states}
- PROCEDURE sendinitiate;
-
- BEGIN
- IF fnlen>0 THEN
- BEGIN
- redopn;
- IF rstatus=open THEN
- BEGIN
- putpar; {Put parameters into buffer}
- makepacket(ascs,seq,filbuf.ln); {Make packet with our parameters}
- numtry:=0;
- state:=sheader;
- END
- ELSE
- BEGIN
- error('Error opening read file');
- state:=kexit;
- END;
- END
- ELSE
- BEGIN
- error('No read file specified');
- state:=kexit;
- END;
- END;
-
- PROCEDURE sendheader;
-
- BEGIN
- IF rcvtyp=ascy THEN
- BEGIN
- IF not sndonly THEN getpar; {Get parameters from ACK of 'S' packet}
- IF rfnlen>0 THEN
- BEGIN
- lintobuf(rfname,rfnlen,filbuf); {Send remote file name.}
- END
- ELSE
- BEGIN
- lintobuf(fname,fnlen,filbuf); {Send local file name.}
- END;
- numtry:=0;
- seq:=(seq+1) mod 64;
- makepacket(ascf,seq,filbuf.ln);
- state:=sdata
- END;
- END;
-
- PROCEDURE senddata;
-
- BEGIN
- IF rcvtyp=ascy THEN
- BEGIN
- getrec;
- numtry:=0;
- seq:=(seq+1) mod 64;
- IF rstatus = open THEN
- BEGIN
- makepacket(ascd,seq,filbuf.ln);
- END
- ELSE
- BEGIN
- makepacket(ascz,seq,0);
- state:=sbreak;
- fnlen:=0;
- END;
- END;
- END;
-
- PROCEDURE sendbreak;
-
- BEGIN
- IF rcvtyp=ascy THEN
- BEGIN
- numtry:=0;
- seq:=(seq+1) mod 64;
- makepacket(ascb,seq,0);
- END;
- state:=wexit;
- END;
- {$F Receive states}
- PROCEDURE receiveinitiate;
-
- BEGIN
- IF rcvtyp=ascs THEN
- BEGIN
- getpar; {Get parameters from packet}
- putpar; {Put parameters into buffer}
- makepacket(ascy,seq,filbuf.ln); {Make ACK packet with our parameters}
- seq:=rcvseq;
- numtry:=0;
- seq:=(seq+1) mod 64;
- state:=rheader;
- END
- ELSE
- BEGIN
- error('Wrong packet in receive initiation');
- state:=kexit;
- END;
- END;
-
- PROCEDURE receiveheader;
-
- BEGIN
- IF rcvtyp=ascf THEN
- BEGIN
- IF fnlen=0 THEN
- BEGIN
- buftolin(rcvbuf,fname,fnlen);
- END;
- IF fnlen>0 THEN
- BEGIN
- wrtopn;
- IF wstatus=open THEN
- BEGIN
- makepacket(ascy,seq,0);
- numtry:=0;
- seq:=(seq+1) mod 64;
- state:=rdata;
- END
- ELSE
- BEGIN
- error('Error opening write file');
- state:=kexit;
- END;
- END
- ELSE
- BEGIN
- error('No output file specified');
- state:=kexit;
- END;
- END
- ELSE IF rcvtyp=ascb THEN
- BEGIN
- makepacket(ascy,seq,0);
- sndpkt;
- state:=kexit;
- END
- ELSE
- BEGIN
- error('Wrong packet receiveing file header');
- state:=kexit;
- END;
- END;
-
- PROCEDURE receivedata;
-
- BEGIN
- IF rcvtyp=ascd THEN
- BEGIN
- putrec(rcvbuf);
- makepacket(ascy,seq,0);
- numtry:=0;
- seq:=(seq+1) mod 64;
- END
- ELSE IF rcvtyp=ascz THEN
- BEGIN
- wrtcls;
- fnlen:=0;
- makepacket(ascy,seq,0);
- numtry:=0;
- seq:=(seq+1) mod 64;
- state:=rheader;
- END
- ELSE
- BEGIN
- error('Unexpected packet receiving data');
- state:=kexit;
- END;
- END;
- {$F Error processing}
-
- {Process fatal errors}
-
- PROCEDURE error; {parameters appear above in forward reference}
- VAR i,l:integer;
-
- BEGIN
- l:=length(msg);
- IF l>maxbuf-6 THEN l:=maxbuf-6;
- bufinit(filbuf);
- FOR i:=1 to 3 DO putbuf(filbuf,ascsp); {Make message readable in packet}
- FOR i:=1 to l DO putbuf(filbuf,ord(msg[i]));
- FOR i:=1 to 3 DO putbuf(filbuf,ascsp); {Make message readable in packet}
- makepacket(asce,seq,filbuf.ln);
- sndpkt;
- state:=kexit;
- END;
- {$F Command state}
- PROCEDURE kermitcommand;
-
- BEGIN
- REPEAT
- rcvpkt;
- IF rcvseq>-1 THEN
- BEGIN
- IF rcvtyp=ascs THEN
- BEGIN
- state:=rinitiate;
- END
- ELSE IF rcvtyp=ascr THEN
- BEGIN
- IF fnlen=0 THEN
- BEGIN
- buftolin(rcvbuf,fname,fnlen);
- END;
- state:=sinitiate;
- END
- ELSE
- BEGIN
- error('Unexpected packet type');
- END;
- END
- ELSE IF rcvseq=-1 THEN
- BEGIN
- writeln('No commands implemented');
- END
- ELSE IF rcvseq=-2 THEN
- BEGIN
- state:=kexit;
- server:=false;
- END;
- UNTIL state<>kcommand;
- END;
- {$F Get processor call options and file specifications}
-
- PROCEDURE getoptions; {System dependent}
-
- BEGIN
- getspec(1,fname,fnlen); {Get local file name, if any.}
- getspec(2,rfname,rfnlen); {Get remote file name, if any.}
- IF 'S' IN options THEN state:=sinitiate;
- IF 'R' IN options THEN state:=rinitiate;
- IF 'T' IN options THEN
- BEGIN
- sndonly:=true;
- state:=sinitiate;
- server:=false;
- END;
- IF 'B' IN options THEN
- BEGIN
- locbsiz:=94;
- END;
- IF 'C' IN options THEN
- BEGIN
- crlfeol:=false;
- creol:=true;
- lfeol:=false;
- END;
- IF 'L' IN options THEN
- BEGIN
- sndlog:=true;
- rcvlog:=true;
- wrtlog:=true;
- redlog:=true;
- END;
- optqu8:=0; {Assume no eight-bit quoting will be done}
- IF 'Q' IN options THEN
- BEGIN
- optqu8:=ascamp; {Eight-bit quoting may be done}
- END;
- IF ('W' IN options) AND ('S' IN options) THEN
- BEGIN
- a1:=30000;
- er(48{TWAIT$},a0,a1);
- END;
- END;
- {$F Initialization state}
- PROCEDURE kermitinitialize;
- VAR lstat: boolean;
-
- BEGIN
- state:=kcommand;
- numtry:=0;
- seq:=0;
- fnlen:=0; {Indicate no file name yet}
- bstrip:=true;
-
- locbsiz:=78;
- loctout:=12;
- locnpad:=0;
- locpad:=0;
- loceol:=asccr;
- locquo:=ascns;
- { locqu8 will be set after options are processed. }
- locrep:=asctil; {Initialize to 0 to turn off repeat counts}
-
- rembsiz:=78;
- remdsiz:=rembsiz-3;
- remtout:=12;
- remnpad:=0;
- rempad:=0;
- remeol:=asccr;
- remqu8:=0;
- remrep:=0;
-
- bufinit(sndbuf);
-
- {The following should only be done on the first call to initialize}
- IF iniflg=false THEN
- BEGIN
- sndonly:=false;
- sndlog:=false;
- rcvlog:=false;
- wrtlog:=false;
- redlog:=false;
- crlfeol:=true;
- creol:=false;
- lfeol:=false;
- rstatus:=closed;
- wstatus:=closed;
- lstatus:=closed;
-
- {System dependent initialization}
- ineoln:=false; {Indicate no readln necessary for first line}
- getoptions; {Process options and file specifications}
- IF sndlog OR rcvlog OR wrtlog OR redlog THEN logopn
- END;
- locqu8:=optqu8; {Eight-bit quoting done only with Q-option}
- iniflg:=true;
- END;
- {$F Main block}
-
-
- BEGIN
- version:= '2.0';
- writeln(output,'Kermit 1100 ',version);
- iniflg:=false;
- server:=true;
- WHILE server DO
- BEGIN
- kermitinitialize;
- IF state=kcommand THEN kermitcommand;
- IF state=sinitiate THEN sendinitiate;
- IF state=rinitiate THEN receiveinitiate;
- WHILE state<>kexit DO
- BEGIN
- REPEAT
- sndpkt;
- numtry:=numtry+1;
- IF sndonly THEN
- BEGIN
- rcvseq:=seq;
- rcvtyp:=ascy;
- rcvbuf.ln:=0;
- END
- ELSE
- BEGIN
- rcvpkt;
- END;
- IF rcvtyp=ascn THEN
- BEGIN
- {We have just received a NAK. The Kermit protocol would
- be much simpler and no less effective if the NAK had never
- been included. However, since this is not universally
- appreciated, one has to deal with them. To do so, we
- will convert a NAK into an ACK with the previous sequence
- number.}
- rcvseq:=(rcvseq-1) mod 64;
- rcvtyp:=ascy;
- END
- ELSE IF rcvseq=-2 THEN {End of file on input}
- BEGIN
- error('End of file on input data');
- state:=kexit;
- server:=false;
- END;
- UNTIL (rcvseq=seq) OR (numtry>=maxtry) OR (state=kexit);
- IF (rcvseq<>seq) AND (state<>kexit) THEN
- BEGIN
- error('Failed to receive expected packet');
- state:=kexit;
- END
- ELSE IF rcvtyp=asce THEN {Just received error packet}
- BEGIN
- state:=kexit
- END
- ELSE
- BEGIN
- CASE state OF
- sheader :sendheader;
- sdata :senddata;
- sbreak :sendbreak;
- rheader :receiveheader;
- rdata :receivedata;
- wexit :state:=kexit; {Go around one more time, then exit}
- kexit :;
- END;
- END
- END;
- wrtcls;
- END;
- logcls;
- writeln('Kermit End');
- END .
-